    var DataHelper = function(inputObj) {
        this.idHeaders = [];
        for (var i = 0; i < inputObj.idHeaders.length; i++) {
            this.idHeaders.push(inputObj.idHeaders[i]["dataName"]);
        }
        this.dataCursorSize = inputObj.dataCursorSize || 25;
        this.numRows = inputObj.numRows;
        this.firstData = [];
        this.lastData = [];
        try {
            this.events = {
                "onnextneeded": inputObj["events"]["onnextneeded"],
                "onpreviousneeded": inputObj["events"]["onpreviousneeded"],
                "onlastneeded": inputObj["events"]["onlastneeded"],
                "onfirstneeded": inputObj["events"]["onfirstneeded"]
            };
        } catch (ex) {
            throw "The handlers for the events onfirstneeded, onnextneeded, onpreviousneeded, onlastneeded are mandatory";
        }
        this.allDataFetched = false;
    };

    DataHelper.prototype = (function() {
        /**
         * Returns the first set of data
         * @return {[Array]}
         */
        var getFirstData = function() {
            return getExtremeData("first", "onfirstneeded", this.firstData, this);
        };

        var getLastData = function() {
            return getExtremeData("last", "onlastneeded", this.lastData, this);
        };

        function getExtremeData(extreme, eventName, array, helperObj) {
            helperObj.recentExtreme = extreme;
            helperObj.currentPageNumber = 0;
            if (array.length == 0) {
                var data = helperObj.events[eventName]();
                if(!data) {
                    return [];
                }
                extreme == "last" && data.reverse();
                appendData(array, data, helperObj.numRows);
            }
            return getDataSlice(array, 0, extreme == "last");
        }

        var getNextData = function() {
            return getNextDataSet(this, "next");
        };

        var getPreviousData = function() {
            return getNextDataSet(this, "previous");
        };

        function getNextDataSet(helperObj, currentAction) {
            var currentExtreme = helperObj.recentExtreme;
            if (currentExtreme == "first") {
                var nextEvent = "onnextneeded";
                var dataSet = helperObj.firstData;
                var otherDataSet = helperObj.lastData;
            } else {
                nextEvent = "onpreviousneeded";
                dataSet = helperObj.lastData;
                otherDataSet = helperObj.firstData;
                var needReverse = true;
            }
            if ((currentExtreme == "first" && currentAction == "previous") || (currentExtreme == "last" && currentAction == "next")) {
                var nextPage = helperObj.currentPageNumber - 1;
                var returnData = getDataSlice(dataSet, nextPage, needReverse);
            } else if (helperObj.allDataFetched) {
                nextPage = helperObj.currentPageNumber + 1;
                if(nextPage > getMaxPages(dataSet, helperObj.numRows)) {
                    return [];
                }
                if (currentExtreme == "first" || (currentExtreme == "last" && nextPage < dataSet.length - 1)) {
                    returnData = getDataSlice(dataSet, nextPage, needReverse);
                } else {
                    returnData = helperObj.getFirstData();
                    nextPage = 0;
                }
            } else {
                nextPage = helperObj.currentPageNumber + 1;
                if (!dataSet[nextPage] || dataSet[nextPage].length < helperObj.numRows) {
                    var position = getLastPosition(dataSet);
                    var newData = helperObj.events[nextEvent](position, getLastDataObject(dataSet));
                    needReverse && newData.reverse();
                    var newDataLength = newData.length;
                    if (newDataLength > 0) {
                        var duplicateFound = removeDuplicates(newData, otherDataSet, helperObj.idHeaders);
                        newData.length > 0 && appendData(dataSet, newData, helperObj.numRows);
                        if (duplicateFound || newDataLength < helperObj.dataCursorSize) {
                            helperObj.allDataFetched = true;
                            mergeData(dataSet, otherDataSet, helperObj.numRows);
                        }
                    }
                }
                returnData = getDataSlice(dataSet, nextPage, needReverse);
                if (helperObj.allDataFetched && currentExtreme == "last" && nextPage == dataSet.length - 1) {
                    returnData = helperObj.getFirstData();
                    nextPage = 0;
                }
            }
            helperObj.currentPageNumber = nextPage < 0 ? 0 : nextPage;
            return returnData;
        }

        function getDataSlice(array, position, needReverse) {
            if(position == -1) {
                var dataSlice = [];
            } else {
                dataSlice = array.slice(position, position + 1)[0];
                if (dataSlice) {
                    dataSlice = dataSlice.slice(0);
                    needReverse && dataSlice.reverse();
                }
            }
            return dataSlice || [];
        }

        /**
         * To merge from first data to last data or vice versa
         * @param  {[Array]} fromArray [firstData/lastData]
         * @param  {[Array]} toArray   [lastData/firstData]
         * @param  {[Number]} numRows   [number of rows of the table]
         * @return {[type]}           [description]
         */

        function mergeData(fromArray, toArray, numRows) {
            toArray.splice(0, toArray.length);
            var mergedArray = flatten(fromArray);
            mergedArray.reverse();
            appendData(toArray, mergedArray, numRows);
        }

        function flatten(array) {
            return [].concat.apply([], array);
        }

        function appendData(dataArray, data, numRows) {
            var lastSet = getLastDataSet(dataArray);
            var lastLength = lastSet.length;
            if (lastLength > 0 && lastLength < numRows) {
                var slice = data.splice(0, numRows - lastLength);
                lastSet.push.apply(lastSet, slice);
            }
            while (data.length > numRows) {
                dataArray.push(data.splice(0, numRows));
            }
            data.length > 0 && dataArray.push(data.splice(0));
            return dataArray;
        }

        function removeDuplicates(newData, otherDataSet, idHeaders) {
            var duplicateFound = false;
            var lastData = getLastDataObject(otherDataSet);
            if (lastData) {
                for (var i = 0; i < newData.length; i++) {
                    if (isDuplicate(newData[i], lastData, idHeaders)) {
                        newData.splice(i);
                        newData.reverse();
                        newData.unshift.apply(newData, flatten(otherDataSet));
                        newData.reverse();
                        duplicateFound = true;
                        break;
                    }
                }
            }
            return duplicateFound;
        }

        function getLastPosition(dataArray) {
            return flatten(dataArray).length;
        }

        function getLastDataObject(dataArray) {
            var lastData = dataArray.slice(-1)[0];
            if(lastData) {
                lastData = lastData.slice(-1)[0];
            }
            return lastData;
        }

        function getLastDataSet(dataArray) {
            return dataArray.slice(-1)[0] || [];
        }

        function getMaxPages(dataArray, numRows) {
            return Math.floor(flatten(dataArray).length/numRows);
        }

        function isDuplicate(data1, data2, idHeaders) {
            var duplicateFound = true;
            for (var i = 0; i < idHeaders.length; i++) {
                if (data1[idHeaders[i]] != data2[idHeaders[i]]) {
                    duplicateFound = false;
                    break;
                }
            }
            return duplicateFound;
        }

        var clearData = function() {
            this.recentExtreme = "first";
            this.currentPageNumber = 0;
            this.firstData = [];
            this.lastData = [];
        };

        var getFullData = function () {
            if(!this.allDataFetched) {
                var lastDataArray = flatten(this.lastData);
                removeDuplicates(lastDataArray, this.firstData, this.idHeaders);
                var firstDataArray = flatten(this.firstData);
                return firstDataArray.concat(lastDataArray.reverse());
            } else {
                return flatten(this.firstData);
            }
        };

        var setFirstData = function(data) {
            this.clearData();
            appendData(this.firstData, data, this.numRows);
        }

        return {
            "getFirstData": getFirstData,
            "getFirstData": getFirstData,
            "getLastData": getLastData,
            "getNextData": getNextData,
            "getPreviousData": getPreviousData,
            "clearData": clearData,
            "getFullData" : getFullData,
            "setFirstData" : setFirstData
        };
    })();